package base

import "fmt"
import "jvmgo/ch07/rtda"
import "jvmgo/ch07/rtda/heap"

/*
	首先确定方法参数在局部变量表中占用多少位置。这个数量并不一定
	等于从java代码中看到的参数个数，原因有2个，一是，long和double
	类型的参数要占用2个位置（记住：局部变量表是一个int型的数组实现），二是，
	对于实例方法，java编译器会在参数列表的前面添加一个参数，这个隐藏的参数就是this引用

	依次从方法调用者的操作数栈中弹出，放进被调用方法的局部变量表中，这样参数传递就完成了

	简而言之：方法调用的参数传递就是从方法调用者的栈帧的操作数栈中弹出数据到
		被调用方法的栈帧的局部变量表中

	提醒：当前执行线程的结构如下

		JVM
		  Thread
		    pc
		    Stack
		      Frame
			LocalVars
			OperandStack

*/
func InvokeMethod(invokerFrame *rtda.Frame, method *heap.Method) {
	thread := invokerFrame.Thread()
	newFrame := thread.NewFrame(method)
	thread.PushFrame(newFrame)

	argSlotCount := int(method.ArgSlotCount())
	if argSlotCount > 0 { //由于操作的是slot结构体，所以不需要对long，double类型做特殊处理
		for i := argSlotCount - 1; i >= 0; i-- {
			slot := invokerFrame.OperandStack().PopSlot()
			newFrame.LocalVars().SetSlot(uint(i), slot)
		}
	}

	// hack!
	if method.IsNative() {
		if method.Name() == "registerNatives" {
			thread.PopFrame()
		} else {
			panic(fmt.Sprintf("native method: %v.%v%v\n",
				method.Class().Name(), method.Name(), method.Descriptor()))
		}
	}
}
